package com.stark.commons.spring.web.support.forest.converter;

import com.dtflys.forest.config.ForestConfiguration;
import com.dtflys.forest.utils.ReflectUtils;

import java.lang.reflect.Type;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Date;

/**
 * 支持返回参数为日期类型：{@link Date}、{@link LocalDate}、{@link LocalDateTime}。
 *
 * @author Ben
 * @version 1.0.0
 * @since 2023/8/4
 */
public class DefaultAutoConverter extends com.dtflys.forest.converter.auto.DefaultAutoConverter {

    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");

    private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private static final DateFormat DATETIME_FORMAT1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    private static final DateFormat DATETIME_FORMAT2 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");

    private static final DateFormat DATETIME_FORMAT3 = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");

    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    private static final DateTimeFormatter DATETIME_FORMATTER1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");

    private static final DateTimeFormatter DATETIME_FORMATTER2 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");

    private static final DateTimeFormatter DATETIME_FORMATTER3 = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS");

    public DefaultAutoConverter(ForestConfiguration configuration) {
        super(configuration);
        configuration.setJsonConverter(new ForestJacksonConverter());
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T convertToJavaObject(Object source, Type targetType) {
        if (source instanceof String) {
            Class<?> clazz = ReflectUtils.toClass(targetType);
            if (Date.class.isAssignableFrom(clazz)) {
                return (T) convertToDate(source);
            }
            if (LocalDate.class.isAssignableFrom(clazz)) {
                return (T) convertToLocalDate(source);
            }
            if (LocalDateTime.class.isAssignableFrom(clazz)) {
                return (T) convertToLocalDateTime(source);
            }
        }
        return super.convertToJavaObject(source, targetType);
    }

    private Date convertToDate(Object source) {
        if (source == null) {
            return null;
        }

        String dateStr = ((String) source).replace("\"", "");
        try {
            return DATE_FORMAT.parse(dateStr);
        } catch (ParseException e) {
            // do nonthing
        }
        try {
            return DATETIME_FORMAT.parse(dateStr);
        } catch (ParseException e) {
            // do nonthing
        }
        try {
            return DATETIME_FORMAT1.parse(dateStr);
        } catch (ParseException e) {
            // do nonthing
        }
        try {
            return DATETIME_FORMAT2.parse(dateStr);
        } catch (ParseException e) {
            // do nonthing
        }
        try {
            return DATETIME_FORMAT3.parse(dateStr);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    private LocalDate convertToLocalDate(Object source) {
        if (source == null) {
            return null;
        }

        String dateStr = ((String) source).replace("\"", "");
        try {
            return LocalDate.parse(dateStr, DATE_FORMATTER);
        } catch (DateTimeParseException e) {
            // do nothing
        }
        try {
            return LocalDateTime.parse(dateStr, DATETIME_FORMATTER).toLocalDate();
        } catch (DateTimeParseException e) {
            // do nothing
        }
        try {
            return LocalDateTime.parse(dateStr, DATETIME_FORMATTER1).toLocalDate();
        } catch (DateTimeParseException e) {
            // do nothing
        }
        try {
            return LocalDateTime.parse(dateStr, DATETIME_FORMATTER2).toLocalDate();
        } catch (DateTimeParseException e) {
            // do nothing
        }
        return LocalDateTime.parse(dateStr, DATETIME_FORMATTER3).toLocalDate();
    }

    private LocalDateTime convertToLocalDateTime(Object source) {
        if (source == null) {
            return null;
        }

        String dateStr = ((String) source).replace("\"", "");
        try {
            return LocalDateTime.parse(dateStr, DATETIME_FORMATTER);
        } catch (DateTimeParseException e) {
            // do nothing
        }
        try {
            return LocalDateTime.parse(dateStr, DATETIME_FORMATTER1);
        } catch (DateTimeParseException e) {
            // do nothing
        }
        try {
            return LocalDateTime.parse(dateStr, DATETIME_FORMATTER2);
        } catch (DateTimeParseException e) {
            // do nothing
        }
        return LocalDateTime.parse(dateStr, DATETIME_FORMATTER3);
    }

}
